package ua.org.nuos.sdms.middle.service;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.Validate;
import ua.org.nuos.sdms.middle.dao.GroupDaoBean;
import ua.org.nuos.sdms.middle.dao.UserDaoBean;
import ua.org.nuos.sdms.middle.dao.UserToGroupDaoBean;
import ua.org.nuos.sdms.middle.entity.Group;
import ua.org.nuos.sdms.middle.entity.User;
import ua.org.nuos.sdms.middle.entity.UserToGroup;
import ua.org.nuos.sdms.middle.entity.enums.Role;
import ua.org.nuos.sdms.middle.util.exception.DuplicateNameException;
import ua.org.nuos.sdms.middle.util.exception.NotGroupLeaderException;
import ua.org.nuos.sdms.middle.util.exception.OneGroupLeaderException;
import ua.org.nuos.sdms.middle.vo.GroupSearch;

import javax.annotation.security.RolesAllowed;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import java.util.*;

/**
 * Created with IntelliJ IDEA.
 * User: dio
 * Date: 8/24/12
 * Time: 5:39 PM
 * To change this template use File | Settings | File Templates.
 */
@Stateless
@RolesAllowed({Role.administrator, Role.client})
public class GroupServiceBean implements GroupServiceLocal {

    @EJB(beanName = "UserDaoBean")
    private UserDaoBean userDao;

    @EJB(beanName = "GroupDaoBean")
    private GroupDaoBean groupDao;

    @EJB(beanName = "UserToGroupDaoBean")
    private UserToGroupDaoBean userToGroupDao;

    @EJB(beanName = "AuthenticationBean")
    private AuthenticationLocal authentication;

    @Override
    public void createGroup(Group group) throws DuplicateNameException {
        Validate.notNull(group);

        if (!groupDao.findByName(group.getName()).isEmpty()) {
            throw new DuplicateNameException(group.getName());
        }
        group.setCreateDate(new Date());
        groupDao.create(group);

        attachCurrentUserToGroup(group.getId(), UserToGroup.Status.LEADER);
    }

    @Override
    public void updateGroup(Group group) throws DuplicateNameException, NotGroupLeaderException {
        Validate.notNull(group);
        validateCurrentUserStatus(group.getId());

        Group old = groupDao.findById(group.getId());
        if (old != null && group != null) {
            group.setCreateDate(old.getCreateDate());
        }
        groupDao.update(group);
    }

    @Override
    public Group findGroupById(long groupId) {
        return groupDao.findById(groupId);
    }

    @Override
    public List<Group> listGroupsByName(String name) {
        return groupDao.findByName(name);
    }

    @Override
    public List<Group> listGroupsByOrganisation(String organisation) {
        return groupDao.findByOrganisation(organisation);
    }

    @Override
    public List<Group> listGroupsByUser(long userId) {
        Set<Group> groups = new HashSet<Group>();
        for (UserToGroup userToGroup : userToGroupDao.findByUser(userId)) {
            groups.add(userToGroup.getGroup());
        }
        return new ArrayList<Group>(groups);
    }

    @Override
    public void removeGroup(long groupId) throws NotGroupLeaderException {
        validateCurrentUserStatus(groupId);

        for (UserToGroup userToGroup : userToGroupDao.findByGroup(groupId)) {
            userToGroupDao.remove(userToGroup);
        }
        groupDao.remove(groupId);
    }

    @Override
    public List<Group> listGroups(GroupSearch search) {
        Collection<Group> groups = new HashSet<Group>();
        if (search.getGroupName() != null && !search.getGroupName().isEmpty()) {
            List<Group> ln = groupDao.findByName(search.getGroupName());
            groups = CollectionUtils.intersection(groups, ln);
        }
        if (search.getOrganisationName() != null && !search.getOrganisationName().isEmpty()) {
            List<Group> lo = groupDao.findByOrganisation(search.getOrganisationName());
            groups = CollectionUtils.intersection(groups, lo);
        }
        if (search.getUserName() != null && !search.getUserName().isEmpty()) {
            Set<Group> gs = new HashSet<Group>();
            for (User user : userDao.findByName(search.getUserName())) {
                gs.addAll(listGroupsByUser(user.getId()));
            }
            groups = CollectionUtils.intersection(groups, gs);
        }
        if (search.getUserEmail() != null && !search.getUserEmail().isEmpty()) {
            User u = userDao.findByEmail(search.getUserEmail());
            List<Group> ge = listGroupsByUser(u.getId());
            groups = CollectionUtils.intersection(groups, ge);
        }
        return new ArrayList<Group>(groups);
    }

    @Override
    public void attachCurrentUserToGroup(long groupId, UserToGroup.Status status) {
        User u = authentication.getCurrentUser();
        Group g = groupDao.findById(groupId);
        if (g.getType().equals(Group.Type.OPENED)) {
            userToGroupDao.create(new UserToGroup(u, g, status));
        }
    }

    @Override
    public void attachUserToGroup(long userId, long groupId, UserToGroup.Status status) throws NotGroupLeaderException {
        validateCurrentUserStatus(groupId);

        User u = authentication.getCurrentUser();
        Group g = groupDao.findById(groupId);
        userToGroupDao.create(new UserToGroup(u, g, status));
    }

    @Override
    public void updateStatus(long userId, long groupId, UserToGroup.Status status) throws NotGroupLeaderException, OneGroupLeaderException {
        validateCurrentUserStatus(groupId);

        UserToGroup utg = userToGroupDao.find(userId, groupId);
        if (utg != null) {
            if (utg.getStatus().equals(UserToGroup.Status.LEADER) && !status.equals(UserToGroup.Status.LEADER)) {
                final int minLeadersInGroup = 2;
                List<UserToGroup> list = userToGroupDao.findByGroupAndStatus(groupId, UserToGroup.Status.LEADER);
                if (list.size() < minLeadersInGroup) {
                    throw new OneGroupLeaderException(utg.getGroup());
                }
            }
            utg.setStatus(status);
            userToGroupDao.update(utg);
        }
    }

    @Override
    public void updateCurrentUserStatus(long groupId, UserToGroup.Status status) {
        User user = authentication.getCurrentUser();
        if (UserToGroup.Status.LEADER.equals(status)) {
            throw new RuntimeException("Only leader can add another leader");
        }
        UserToGroup utg = userToGroupDao.find(user.getId(), groupId);
        if (utg != null) {
            utg.setStatus(status);
            userToGroupDao.update(utg);
        }
    }

    @Override
    public UserToGroup.Status findStatus(long userId, long groupId) {
        UserToGroup utg = userToGroupDao.find(userId, groupId);
        if (utg == null) {
            return null;
        } else {
            return utg.getStatus();
        }
    }

    @Override
    public UserToGroup.Status findStatus(long groupId) {
        User user = authentication.getCurrentUser();
        UserToGroup utg = userToGroupDao.find(user.getId(), groupId);
        if (utg == null) {
            return null;
        } else {
            return utg.getStatus();
        }
    }

    @Override
    public void removeUserFromGroup(long userId, long groupId) throws NotGroupLeaderException {
        validateCurrentUserStatus(groupId);

        UserToGroup userToGroup = userToGroupDao.find(userId, groupId);
        userToGroupDao.remove(userToGroup);
    }

    @Override
    public void removeUserFromGroup(long groupId) {
        User user = authentication.getCurrentUser();
        UserToGroup userToGroup = userToGroupDao.find(user.getId(), groupId);
        userToGroupDao.remove(userToGroup);
    }

    private void validateCurrentUserStatus(long groupId) throws NotGroupLeaderException {
        User user = authentication.getCurrentUser();
        UserToGroup.Status status = findStatus(user.getId(), groupId);
        if (status == null) {
            throw new RuntimeException(String.format("User does not attach to this group(%d)", groupId));
        }
        if (!UserToGroup.Status.LEADER.equals(status)) {
            throw new NotGroupLeaderException();
        }
    }
}
